package pl.com.qapps.datastore.berkeley;

import java.io.IOException;

import pl.com.qapps.datastore.BytesHelper;
import pl.com.qapps.datastore.DatastoreUtils;


import com.google.appengine.api.datastore.Key;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.LockMode;
import com.sleepycat.je.OperationStatus;
import com.sleepycat.je.Transaction;

/**
 * 
 * @author Jakub Dykowski
 *
 */
public class AlocateIdsRequest extends BaseRequest<Long> {

	private static final DatabaseEntry ONE = new DatabaseEntry(new byte[] { 0,
			0, 0, 0, 0, 0, 0, 1 });
	private static final DatabaseEntry ROOT = new DatabaseEntry(
			new byte[] { 0 });

	private Key parent;
	private long amount;

	public AlocateIdsRequest() {
	}

	/**
	 * returns the last id
	 */
	@Override
	Long run(Store store, Transaction txn) {
		try {
			if (getTxn() != null)
				throw new IllegalStateException(
						"allocate id request cannot has got txn specified, it runs in new transaction which commits itself transaprently, it is an programming error");

			DatabaseEntry keyData;
			DatabaseEntry nextData = new DatabaseEntry();

			if (parent == null) {
				// root entity
				keyData = ROOT;
			} else {
				// entity group
				byte[] rootBytes = BytesHelper.serialize(DatastoreUtils
						.getEntityGroup(parent));
				keyData = new DatabaseEntry(rootBytes);
			}
			// get next id
			OperationStatus status = store.getIdDb().get(txn, keyData,
					nextData, LockMode.DEFAULT);
			if (status == OperationStatus.NOTFOUND) {
				// initialize id counter for roots or any entity group
				store.getIdDb().put(txn, keyData, ONE);
				// recursively do it again except id counter is initailized now
				return run(store, txn);
			} else {
				long next = BytesHelper.toLong(nextData.getData());
				next += amount;
				// update new counter value
				store.getIdDb().put(txn, keyData,
						new DatabaseEntry(BytesHelper.serialize(next)));
				return --next;
			}
		} catch (IOException ex) {
			throw new RuntimeException("Serialization error", ex);
		}
	}

	@Override
	void init(Store store) {
		// TODO Auto-generated method stub

	}

	void setParent(Key parent) {
		this.parent = parent;
	}

	/**
	 * By default it is set to 1L.
	 * 
	 * @param amount
	 */
	void setAmount(long amount) {
		this.amount = amount;
	}

}
